package com.heaven.core.framework;

import com.heaven.exception.BizCodeEnum;
import com.heaven.exception.RRException;
import com.heaven.utils.BeanUtils;
import com.heaven.utils.ConvertUtils;
import com.mongodb.client.result.DeleteResult;
import com.mongodb.client.result.UpdateResult;
import lombok.Getter;
import org.bson.Document;
import java.lang.reflect.Field;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.annotation.Id;
import org.springframework.data.domain.Example;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.data.mongodb.repository.MongoRepository;

import java.io.Serializable;
import java.util.Collection;
import java.util.List;
import java.util.Optional;

@SuppressWarnings("unchecked")
public class PzServiceImpl<M extends MongoRepository<T, String>, T> implements PzService<T>{

    protected Log logger = LogFactory.getLog(getClass());

    @Autowired
    protected M baseMapper;

    @Getter
    @Autowired
    protected MongoTemplate mongoTemplate;

    protected Class<T> currentModelClass() {
        return (Class<T>) BeanUtils.getSuperClassGenericType(getClass(), 1);
    }

    @Override
    public String getEntityPrimaryKeyFieldName() {
        String idFileName = "";
        for (java.lang.reflect.Field field : getEntityClass().getDeclaredFields()) {
            if (field.isAnnotationPresent(Id.class)) {
                idFileName = field.getName();
            }
        }
        if(idFileName.isEmpty()){
            logger.error(BizCodeEnum.ENTITY_ID_ANNOTATION_EXCEPTION.toString());
            throw new RRException(BizCodeEnum.ENTITY_ID_ANNOTATION_EXCEPTION);
        }
        return idFileName;
    }

    @Override
    public MongoRepository<T, String> getBaseMapper() {
        return this.baseMapper;
    }

    @Override
    public Class<T> getEntityClass() {
        return currentModelClass();
    }

    @Override
    public boolean save(T entity) {
        if (entity == null) {
            return false;
        }
        mongoTemplate.save(entity);
        return true;
    }

    @Override
    public void saveBatch(T... entities) {
        for (T entity : entities) {
            mongoTemplate.insert(entity);
        }
    }

    @Override
    public void saveBatch(List<T> entities) {
        entities.forEach(this::save);
    }

    @Override
    public int deleteById(Serializable id) {
        String ID = ConvertUtils.toStr(id);
        if(ID == null){
            logger.error("mongodb异常-ID为空");
            throw new RRException(BizCodeEnum.MONGODB_ID_NULL_EXCEPTION);
        }
        // 尝试查找并删除，然后检查删除结果
        Optional<?> optionalResult = baseMapper.findById(ID);
        if (optionalResult.isPresent()) {
            // 如果存在，执行删除
            baseMapper.deleteById(ID);
            // 假设 deleteById 方法执行成功即代表删除成功，不进行二次查询
            return 1;
        } else {
            // 如果不存在，返回0
            return 0;
        }
    }

    @Override
    public long del(Query query) {
        DeleteResult remove = mongoTemplate.remove(query, getEntityClass());
        return remove.getDeletedCount();
    }

    @Override
    public int deleteBatchIds(Collection<?> idList) {
        int flag = 0;
        for (Object id : idList) {
            int i = deleteById(ConvertUtils.toStr(id));
            if(i == 1) flag = 1;
        }
        return flag;
    }

    @Override
    public T selectOne(Query query) {
        return mongoTemplate.findOne(query, getEntityClass());
    }

    @Override
    public T selectById(Serializable id) {
        String ID = ConvertUtils.toStr(id);
        if(ID == null){
            logger.error(BizCodeEnum.MONGODB_ID_NULL_EXCEPTION.toString());
            throw new RRException(BizCodeEnum.MONGODB_ID_NULL_EXCEPTION);
        }
        Optional<T> oneById = baseMapper.findById(ID);
        return oneById.orElse(null);
    }

    @Override
    public List<T> selectBatchIds(Collection<? extends Serializable> idList) {
        Query query = new Query();
        query.addCriteria(Criteria.where(getEntityPrimaryKeyFieldName()).in(idList));
        return mongoTemplate.find(query, getEntityClass());
    }

    @Override
    public List<T> selectList(T example) {
        if(example == null){
            return null;
        }
        return baseMapper.findAll(Example.of(example));
    }

    @Override
    public List<T> selectList(Query query) {
        return mongoTemplate.find(query, getEntityClass());
    }

    @Override
    public List<T> selectList(Query query, int pageSize, long currentPage) {
        if(currentPage >=1 && pageSize > 0) {
            query.skip((currentPage-1)*pageSize).limit(pageSize);
            return selectList(query);
        } else {
            logger.error(BizCodeEnum.PAGE_SEARCH_ARGS_EXCEPTION.toString());
            throw new RRException(BizCodeEnum.PAGE_SEARCH_ARGS_EXCEPTION);
        }
    }

    @Override
    public Long selectCount(Query query) {
        return mongoTemplate.count(query, getEntityClass());
    }

    @Override
    public List<T> selectSort(Query query, Sort sort) {
        return mongoTemplate.find(query.with(sort), getEntityClass());
    }

    @Override
    public UpdateResult updateOne(Query query, Update update) {
        return mongoTemplate.updateFirst(query, update, getEntityClass());
    }

    @Override
    public UpdateResult updateOne(Query query, String field, Object value) {
        return mongoTemplate.updateFirst(query, Update.update(field, value), getEntityClass());
    }

    @Override
    public UpdateResult updateMulti(Query query, Update update) {
        return mongoTemplate.updateMulti(query, update, getEntityClass());
    }

    @Override
    public UpdateResult updateById(Serializable id, T entity) {
        String ID = ConvertUtils.toStr(id);
        if(ID == null){
            logger.error("mongodb异常-ID为空");
            throw new RRException(BizCodeEnum.MONGODB_ID_NULL_EXCEPTION);
        }
        // 尝试查找并更新，然后检查更新结果
        Optional<?> optionalResult = baseMapper.findById(ID);
        if (optionalResult.isPresent()) {
            // 如果存在，执行更新
            return mongoTemplate.updateFirst(new Query().addCriteria(Criteria
                    .where("_id").is(ID)),
                    Update.fromDocument(convertToDocument(entity)), getEntityClass());
        } else {
            // 如果不存在，返回未知更新
            return UpdateResult.unacknowledged();
        }
    }

    /**
     * 将Java对象转换为MongoDB的Document对象
     *
     * @param entity 要转换的Java对象
     * @return 转换后的Document对象
     * @throws RRException 如果在访问对象字段时发生异常，则抛出RRException
     */
    private static Document convertToDocument(Object entity){
        if(entity == null){
            return null;
        }
        Document document = new Document();
        Class<?> clazz = entity.getClass();

        for (Field field : clazz.getDeclaredFields()) {
            field.setAccessible(true); // 设置为可访问，以便访问私有字段
            Object value;
            try {
                value = field.get(entity);
            } catch (IllegalAccessException e) {
                throw new RRException(e);
            }
            document.append(field.getName(), value);
        }
        return document;
    }

}
